/**
 * $Id: rand.c,v 1.10 2005/11/05 21:17:55 ton Exp $
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

#include "../LSCM/MeshBuilderStdafx.h"

#include <stdlib.h>
#include <string.h>

#include "MEM_guardedalloc.h"

#include "PIL_time.h"
#include "BLI_rand.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(WIN32) && !defined(FREE_WINDOWS)
typedef unsigned __int64	r_uint64;

#define MULTIPLIER	0x5DEECE66Di64 
#define MASK		0x0000FFFFFFFFFFFFi64
#else
typedef unsigned long long	r_uint64;

#define MULTIPLIER	0x5DEECE66Dll
#define MASK		0x0000FFFFFFFFFFFFll
#endif

#define ADDEND		0xB

#define LOWSEED		0x330E


unsigned char hash[512]= {
0xA2,0xA0,0x19,0x3B,0xF8,0xEB,0xAA,0xEE,0xF3,0x1C,0x67,0x28,0x1D,0xED,0x0,0xDE,0x95,0x2E,0xDC,0x3F,0x3A,0x82,0x35,0x4D,0x6C,0xBA,0x36,0xD0,0xF6,0xC,0x79,0x32,0xD1,0x59,0xF4,0x8,0x8B,0x63,0x89,0x2F,0xB8,0xB4,0x97,0x83,0xF2,0x8F,0x18,0xC7,0x51,0x14,0x65,0x87,0x48,0x20,0x42,0xA8,0x80,0xB5,0x40,0x13,0xB2,0x22,0x7E,0x57,
0xBC,0x7F,0x6B,0x9D,0x86,0x4C,0xC8,0xDB,0x7C,0xD5,0x25,0x4E,0x5A,0x55,0x74,0x50,0xCD,0xB3,0x7A,0xBB,0xC3,0xCB,0xB6,0xE2,0xE4,0xEC,0xFD,0x98,0xB,0x96,0xD3,0x9E,0x5C,0xA1,0x64,0xF1,0x81,0x61,0xE1,0xC4,0x24,0x72,0x49,0x8C,0x90,0x4B,0x84,0x34,0x38,0xAB,0x78,0xCA,0x1F,0x1,0xD7,0x93,0x11,0xC1,0x58,0xA9,0x31,0xF9,0x44,0x6D,
0xBF,0x33,0x9C,0x5F,0x9,0x94,0xA3,0x85,0x6,0xC6,0x9A,0x1E,0x7B,0x46,0x15,0x30,0x27,0x2B,0x1B,0x71,0x3C,0x5B,0xD6,0x6F,0x62,0xAC,0x4F,0xC2,0xC0,0xE,0xB1,0x23,0xA7,0xDF,0x47,0xB0,0x77,0x69,0x5,0xE9,0xE6,0xE7,0x76,0x73,0xF,0xFE,0x6E,0x9B,0x56,0xEF,0x12,0xA5,0x37,0xFC,0xAE,0xD9,0x3,0x8E,0xDD,0x10,0xB9,0xCE,0xC9,0x8D,
0xDA,0x2A,0xBD,0x68,0x17,0x9F,0xBE,0xD4,0xA,0xCC,0xD2,0xE8,0x43,0x3D,0x70,0xB7,0x2,0x7D,0x99,0xD8,0xD,0x60,0x8A,0x4,0x2C,0x3E,0x92,0xE5,0xAF,0x53,0x7,0xE0,0x29,0xA6,0xC5,0xE3,0xF5,0xF7,0x4A,0x41,0x26,0x6A,0x16,0x5E,0x52,0x2D,0x21,0xAD,0xF0,0x91,0xFF,0xEA,0x54,0xFA,0x66,0x1A,0x45,0x39,0xCF,0x75,0xA4,0x88,0xFB,0x5D,
0xA2,0xA0,0x19,0x3B,0xF8,0xEB,0xAA,0xEE,0xF3,0x1C,0x67,0x28,0x1D,0xED,0x0,0xDE,0x95,0x2E,0xDC,0x3F,0x3A,0x82,0x35,0x4D,0x6C,0xBA,0x36,0xD0,0xF6,0xC,0x79,0x32,0xD1,0x59,0xF4,0x8,0x8B,0x63,0x89,0x2F,0xB8,0xB4,0x97,0x83,0xF2,0x8F,0x18,0xC7,0x51,0x14,0x65,0x87,0x48,0x20,0x42,0xA8,0x80,0xB5,0x40,0x13,0xB2,0x22,0x7E,0x57,
0xBC,0x7F,0x6B,0x9D,0x86,0x4C,0xC8,0xDB,0x7C,0xD5,0x25,0x4E,0x5A,0x55,0x74,0x50,0xCD,0xB3,0x7A,0xBB,0xC3,0xCB,0xB6,0xE2,0xE4,0xEC,0xFD,0x98,0xB,0x96,0xD3,0x9E,0x5C,0xA1,0x64,0xF1,0x81,0x61,0xE1,0xC4,0x24,0x72,0x49,0x8C,0x90,0x4B,0x84,0x34,0x38,0xAB,0x78,0xCA,0x1F,0x1,0xD7,0x93,0x11,0xC1,0x58,0xA9,0x31,0xF9,0x44,0x6D,
0xBF,0x33,0x9C,0x5F,0x9,0x94,0xA3,0x85,0x6,0xC6,0x9A,0x1E,0x7B,0x46,0x15,0x30,0x27,0x2B,0x1B,0x71,0x3C,0x5B,0xD6,0x6F,0x62,0xAC,0x4F,0xC2,0xC0,0xE,0xB1,0x23,0xA7,0xDF,0x47,0xB0,0x77,0x69,0x5,0xE9,0xE6,0xE7,0x76,0x73,0xF,0xFE,0x6E,0x9B,0x56,0xEF,0x12,0xA5,0x37,0xFC,0xAE,0xD9,0x3,0x8E,0xDD,0x10,0xB9,0xCE,0xC9,0x8D,
0xDA,0x2A,0xBD,0x68,0x17,0x9F,0xBE,0xD4,0xA,0xCC,0xD2,0xE8,0x43,0x3D,0x70,0xB7,0x2,0x7D,0x99,0xD8,0xD,0x60,0x8A,0x4,0x2C,0x3E,0x92,0xE5,0xAF,0x53,0x7,0xE0,0x29,0xA6,0xC5,0xE3,0xF5,0xF7,0x4A,0x41,0x26,0x6A,0x16,0x5E,0x52,0x2D,0x21,0xAD,0xF0,0x91,0xFF,0xEA,0x54,0xFA,0x66,0x1A,0x45,0x39,0xCF,0x75,0xA4,0x88,0xFB,0x5D,
};

/***/

struct RNG {
	r_uint64 X;
};

RNG	*rng_new(unsigned int seed)
{
	RNG *rng = (RNG*)MEM_mallocN(sizeof(*rng), "rng");

	rng_seed(rng, seed);

	return rng;
}

void rng_free(RNG* rng)
{
	MEM_freeN(rng);
}

void rng_seed(RNG *rng, unsigned int seed) {
	rng->X= (((r_uint64) seed)<<16) | LOWSEED;
}

int rng_getInt(RNG *rng) {
	rng->X= (MULTIPLIER*rng->X + ADDEND)&MASK;
	return (int) (rng->X>>17);
}

double rng_getDouble(RNG *rng) {
	return (double) rng_getInt(rng)/0x80000000;
}

float rng_getFloat(RNG *rng) {
	return (float) rng_getInt(rng)/0x80000000;
}

void rng_shuffleArray(RNG *rng, void *data, int elemSize, int numElems)
{
	int i = numElems;
	void *temp = malloc(elemSize);

	while (--i) {
		int j = rng_getInt(rng)%numElems;
		if(i!=j) {
			void *iElem = (unsigned char*)data + i*elemSize;
			void *jElem = (unsigned char*)data + j*elemSize;
			memcpy(temp, iElem, elemSize);
			memcpy(iElem, jElem, elemSize);
			memcpy(jElem, temp, elemSize);
		}
	}

	free(temp);
}

/***/

static RNG theBLI_rng = {0};

/* note, this one creates periodical patterns */
void BLI_srand(unsigned int seed) {
	rng_seed(&theBLI_rng, seed);
}

/* using hash table to create better seed */
void BLI_srandom(unsigned int seed) {
	extern unsigned char hash[];	// noise.c
	
	rng_seed(&theBLI_rng, seed + hash[seed & 255]);
	seed= rng_getInt(&theBLI_rng);
	rng_seed(&theBLI_rng, seed + hash[seed & 255]);
	seed= rng_getInt(&theBLI_rng);
	rng_seed(&theBLI_rng, seed + hash[seed & 255]);
}

int BLI_rand(void) {
	return rng_getInt(&theBLI_rng);
}

double BLI_drand(void) {
	return rng_getDouble(&theBLI_rng);
}

float BLI_frand(void) {
	return rng_getFloat(&theBLI_rng);
}

void BLI_fillrand(void *addr, int len) {
	RNG rng;
	unsigned char *p= (unsigned char*)addr;

	rng_seed(&rng, (unsigned int) (PIL_check_seconds_timer()*0x7FFFFFFF));
	while (len--) *p++= rng_getInt(&rng)&0xFF;
}

void BLI_array_randomize(void *data, int elemSize, int numElems, unsigned int seed)
{
	RNG rng;

	rng_seed(&rng, seed);
	rng_shuffleArray(&rng, data, elemSize, numElems);
}

/* ********* for threaded random ************** */
#define MAX_RNG_THREADS		16

static RNG rng_tab[MAX_RNG_THREADS];

void BLI_thread_srandom(int thread, unsigned int seed)
{
	extern unsigned char hash[];	// noise.c
	
	rng_seed(&rng_tab[thread], seed + hash[seed & 255]);
	seed= rng_getInt(&rng_tab[thread]);
	rng_seed(&rng_tab[thread], seed + hash[seed & 255]);
	seed= rng_getInt(&rng_tab[thread]);
	rng_seed(&rng_tab[thread], seed + hash[seed & 255]);
}

int BLI_thread_rand(int thread) {
	return rng_getInt(&rng_tab[thread]);
}

float BLI_thread_frand(int thread) {
	return rng_getFloat(&rng_tab[thread]);
}


